package cn.indi.zyj.business.service;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.indi.zyj.business.domain.*;
import cn.indi.zyj.business.dto.ConfirmOrderMQDto;
import cn.indi.zyj.business.enums.ConfirmOrderStatusEnum;
import cn.indi.zyj.business.enums.RedisKeyPreEnum;
import cn.indi.zyj.business.enums.RocketMQTopicEnum;
import cn.indi.zyj.business.enums.SeatColEnum;
import cn.indi.zyj.business.feign.MemberFeign;
import cn.indi.zyj.business.mapper.ConfirmOrderMapper;
import cn.indi.zyj.business.mapper.DailyTrainSeatMapper;
import cn.indi.zyj.business.mapper.cust.DailyTrainTicketMapperCust;
import cn.indi.zyj.common.context.LoginMemberContext;
import cn.indi.zyj.common.exception.BusinessException;
import cn.indi.zyj.common.exception.BusinessExceptionEnum;
import cn.indi.zyj.common.req.MemberTicketReq;
import cn.indi.zyj.common.resp.CommonResp;
import cn.indi.zyj.common.util.SnowUtil;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.fastjson.JSON;
import jakarta.annotation.Resource;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
public class BeforeConfirmOrderService {

    private static final Logger LOG = LoggerFactory.getLogger(BeforeConfirmOrderService.class);


    @Resource
    private ConfirmOrderMapper confirmOrderMapper;

    @Resource
    private DailyTrainTicketService dailyTrainTicketService;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private SkTokenService skTokenService;

    @Resource
    private RocketMQTemplate rocketMQTemplate;

    @SentinelResource(value = "doConfirm", blockHandler = "doConfirmBlock")
    public Long doOrder(ConfirmOrderDoReq req) {

        req.setMemberId(LoginMemberContext.getId());
        // 校验令牌余量
        boolean validSkToken = skTokenService.validSkToken(req.getDate(), req.getTrainCode(), LoginMemberContext.getId());
        if (validSkToken) {
            LOG.info("令牌校验通过");
        } else {
            LOG.info("令牌校验不通过");
            throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_SK_TOKEN_FAIL);
        }
        String lockKey = RedisKeyPreEnum.CONFIRM_ORDER + DateUtil.formatDate(req.getDate()) + "-" + req.getTrainCode();


//        Boolean setIfAbsent = redisTemplate.opsForValue().setIfAbsent(lockKey, lockKey, 5, TimeUnit.SECONDS);
//
//        if (setIfAbsent){
//            LOG.info("恭喜！抢到锁了！");
//        } else {
//            LOG.info("很遗憾，未能抢到锁...");
//            throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_LOCK_FAIL);
//        }
//        RLock lock = null;
//        try {
//            lock = redissonClient.getLock(lockKey);
//            // 看门狗默认等待30s，未结束会重新刷新
//            boolean tryLock = lock.tryLock(0, TimeUnit.SECONDS);
//            if (tryLock){
//                LOG.info("恭喜！抢到锁了！");
//            } else {
//                LOG.info("很遗憾，未能抢到锁...");
//                throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_LOCK_FAIL);
//            }
//        } catch (InterruptedException e){
//            LOG.info("购票异常", e);
//        }finally {
//            LOG.info("购票结束，释放锁。");
//            if (lockKey!=null && lock.isHeldByCurrentThread()){
//                lock.unlock();
//            }
//        }

        // 保存确认订单表，状态初始
        Date now = new Date();
        ConfirmOrder confirmOrder = new ConfirmOrder();
        confirmOrder.setId(SnowUtil.getSnowflakeNextId());
        confirmOrder.setMemberId(req.getMemberId());
        Date date = req.getDate();
        confirmOrder.setDate(date);
        String trainCode = req.getTrainCode();
        confirmOrder.setTrainCode(trainCode);
        String start = req.getStart();
        confirmOrder.setStart(start);
        String end = req.getEnd();
        confirmOrder.setEnd(end);
        confirmOrder.setDailyTrainTicketId(req.getDailyTrainTicketId());
        confirmOrder.setStatus(ConfirmOrderStatusEnum.INIT.getCode());
        confirmOrder.setCreateTime(now);
        confirmOrder.setUpdateTime(now);
        List<ConfirmOrderTicketReq> tickets = req.getTickets();
        confirmOrder.setTickets(JSON.toJSONString(tickets));
        confirmOrderMapper.insert(confirmOrder);



        // TODO 可以购票发送MQ
        ConfirmOrderMQDto confirmOrderMQDto = new ConfirmOrderMQDto();
        confirmOrderMQDto.setDate(date);
        confirmOrderMQDto.setTrainCode(trainCode);
        confirmOrderMQDto.setLogId(MDC.get("LOG_ID"));
        String reqJson = JSON.toJSONString(confirmOrderMQDto);
        LOG.info("排队购票，发送MQ消息队列开售{}", reqJson);
        rocketMQTemplate.convertAndSend(RocketMQTopicEnum.CONFIRM_ORDER.getCode(), reqJson);
        LOG.info("排队购票，发送MQ结束");

        return confirmOrder.getId();
    }

    /**
     * 降级方法，需包含限流方法的所有参数和BlockException参数
     * @param req
     * @param e
     */
    public void doConfirmBlock(ConfirmOrderDoReq req, BlockException e) {
        LOG.info("购票请求被限流：{}", req);
        throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_FLOW_EXCEPTION);
    }
}
